// =============================================================
// CSpaceService
// ---------------------------------------
// Author : Woo-Hyun Kim
// Email : woorung@icu.ac.kr or woorung@nhncorp.com
// Date : Oct. 8, 2007
// =============================================================
#include "CSpaceService.h"

CSpaceService::CSpaceService(char *name) : CService(name) 
{
	this->cube_space = new CSpace(name);
}

CSpaceService::CSpaceService(const CSpaceService &rhs)  : CService(rhs)
{
/*
	this->setName((char *)rhs.getName());
	this->instance((char *)rhs.instance());
*/

	// in order to use only one space
	this->cube_space = rhs.cube_space;
}

CSpaceService::~CSpaceService() 
{
	// no free in case of using only one space
	//delete this->cube_space;
}

CSpaceService *CSpaceService::clone() const 
{
	return new CSpaceService(*this);
}

// Note:
// the difference to clone() is not to share space
// that is, the instance from newInstance() will be different
// Refer to invoke() & initialize() in CServiceRepository class
CSpaceService *CSpaceService::newInstance() const 
{
	char tmp_name[20];
	sprintf(tmp_name, "spaceservice.%d", pthread_self());

	return new CSpaceService(tmp_name);
}

CSpaceService &CSpaceService::operator=(const CSpaceService &rhs) 
{
	this->setName((char *)rhs.getName());
	this->instance((char *)rhs.instance());

	// in order to use only one space
	this->cube_space = rhs.cube_space;

	return *this;
}

int CSpaceService::parameterize(CObjectArray *objs, map<string,string> &params)
{
	int i=0;
	for(i=0;i<objs->__size;i++) {
		if(objs->__ptr[i]==NULL) break;

		cube__CKeyValue *kv = dynamic_cast<cube__CKeyValue *>(objs->__ptr[i]);
		if(kv==NULL) return -1;

#ifdef DEBUG
		cout << "param[" << kv->getKey() << "]=" << kv->getValue() << endl;
#endif

		params[kv->getKey()] = kv->getValue();
	}

	if(i==0) return -1;
	// the position of real data after parameters
	else return i+1;
}

int CSpaceService::incarnate(CObjectArray *&pobjs, vector< CKeyValue * > &bulk_kv)
{
	pobjs->__size = bulk_kv.size();
	pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

	for(int i=0;i<pobjs->__size;i++) {
		CKeyValue *keyval = bulk_kv[i];

		pobjs->__ptr[i] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);

#ifdef DEBUG
		cout << "#######################################" << endl;
		cout << "found key = " << keyval->getKey() << endl;
		cout << "found value = " << keyval->getValue() << endl;
		cout << "#######################################" << endl;
#endif 

		dynamic_cast<cube__CKeyValue*>(pobjs->__ptr[i])->set(keyval->getKey(), keyval->getValue());

		//dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[i])->setKey(keyval->getKey());
		//dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[i])->setValue(keyval->getValue());
	}

	return pobjs->__size;
}

int CSpaceService::write(map<string,string> &params, CObjectArray *objs, int start, CObjectArray *&pobjs)
{
	for(int i=start;i<objs->__size;i++) {
		cube__CObject *what = objs->__ptr[i];

		if(typeid(*what)==typeid(cube__CKeyValue)) {
			cube__CKeyValue *rkeyval = dynamic_cast<cube__CKeyValue *>(objs->__ptr[i]);
			if(rkeyval==NULL) return -1;

			CKeyValue keyval;
			keyval.set(rkeyval->getKey(), rkeyval->getValue());
			//keyval.setValue(rkeyval->getValue());
#ifdef DEBUG
			cout << "///space service///writing key = " << rkeyval->getKey() << ", value = " << rkeyval->getValue() << endl;
#endif
			cube_space->write(&keyval);
		}
		else {
			cout << "##################################################################" << endl;
			cout << "Error :  type is not allowed" << endl;
			cout << "##################################################################" << endl;

			pobjs->__size = 0;
			pobjs->__ptr = NULL;

			return -1;
		}
	}

	pobjs->__size = 1;
	pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

	pobjs->__ptr[0] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);
	char size[100];
	sprintf(size, "%d", cube_space->size());
	dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[0])->setKey(CUBE_SPACE_SIZE);
	dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[0])->setValue(size);

#ifdef DEBUG
	vector< CSmartTemplate<CObject> > all;
	all = cube_space->read(NULL, all);
	cout << "-------------- write : all objects ---------------" << endl;
	cout << "size = " << all.size() << endl;
	for(int i=0;i<all.size();i++) dynamic_cast<CKeyValue &>(*(all[i])).print();
	cout << "-------------------------- ---------------" << endl << endl;
#endif

	return 1;
}

int CSpaceService::read(map<string,string> &params, CObjectArray *objs, int start, CObjectArray *&pobjs, bool wait)
{
	cube__CObject *data_key = NULL;
	if(objs->__ptr[start]!=NULL) data_key = objs->__ptr[start];

#ifdef DEBUG
	if(data_key!=NULL) cout << "reading key = " << dynamic_cast<cube__CKeyValue *>(data_key)->getKey() << endl;
#endif

	if(data_key==NULL||typeid(*data_key)==typeid(cube__CKeyValue)) {
		CKeyValue key;
		CKeyValue *keyval = NULL;

		if(data_key!=NULL) {
			key.setKey(dynamic_cast<cube__CKeyValue *>(data_key)->getKey());

			if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_READ) keyval = dynamic_cast<CKeyValue *>(cube_space->read(&key, keyval));
			else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_READ_IF_EXISTS) keyval = dynamic_cast<CKeyValue *>(cube_space->readIfExists(&key, keyval));
		}
		else {
			if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_READ) keyval = dynamic_cast<CKeyValue *>(cube_space->read(keyval));
			if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_READ_IF_EXISTS) keyval = dynamic_cast<CKeyValue *>(cube_space->readIfExists(keyval));
		}

		if(keyval==NULL) {
#ifdef DEBUG
			if(data_key!=NULL) cout << "there is nothing to read by key[" << dynamic_cast<CKeyValue *>(data_key)->getKey();
			else cout << "there is nothing to read by key[anykey";
			cout << "]: current size = " << cube_space->size() << endl;
#endif
			pobjs->__size = 0;
			pobjs->__ptr = NULL;

			return -1;
		}

		pobjs->__size = 1;
		pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

		pobjs->__ptr[0] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);

#ifdef DEBUG
		cout << "#######################################" << endl;
		cout << "found key = " << keyval->getKey() << endl;
		cout << "found value = " << keyval->getValue() << endl;
		cout << "#######################################" << endl;
#endif 

		dynamic_cast<cube__CKeyValue*>(pobjs->__ptr[0])->set(keyval->getKey(), keyval->getValue());

		delete keyval;
	}
	else {
#ifdef DEBUG
		cout << "type is not correct : " << typeid(*data_key) << endl;
#endif
		pobjs->__size = 0;
		pobjs->__ptr = NULL;

		return -1;
	}
	

#ifdef DEBUG
	vector< CSmartTemplate<CObject> > all;
	all = cube_space->read(NULL, all);
	cout << "-------------- read : all objects ---------------" << endl;
	cout << "size = " << all.size() << endl;
	for(int i=0;i<all.size();i++) {
		if(typeid(*data_key)==typeid(cube__CMessage)) dynamic_cast<CMessage &>(*(all[i])).print();
		else if(typeid(*data_key)==typeid(cube__CKeyValue)) dynamic_cast<CKeyValue &>(*(all[i])).print();
	}
	cout << "-------------------------- ---------------" << endl << endl;
#endif

	return 1;
}

int CSpaceService::take(map<string,string> &params, CObjectArray *objs, int start, CObjectArray *&pobjs, bool wait)
{
	cube__CObject *data_key = NULL;
	if(objs->__ptr[start]!=NULL) data_key = objs->__ptr[start];

#ifdef DEBUG
	if(data_key!=NULL) cout << "taking key = " << dynamic_cast<cube__CKeyValue *>(data_key)->getKey() << endl;
	else cout << "taking key = NULL" << endl;
#endif

	if(data_key==NULL||typeid(*data_key)==typeid(cube__CKeyValue)) {
		CKeyValue key;
		CKeyValue *keyval = NULL;

		int bulk_limit = -1;
		if(params[CUBE_SPACE_BULK_SIZE]!="") bulk_limit  = atoi((char *)params[CUBE_SPACE_BULK_SIZE].c_str());

#ifdef DEBUG
		cout << "bulk_limit = " << bulk_limit << endl;
#endif

		vector< CSmartTemplate<CObject> > bulk_kv;

		if(bulk_limit<0&&data_key!=NULL) { // find and add
			key.setKey(dynamic_cast<cube__CKeyValue *>(data_key)->getKey());

			if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE) {
				keyval = dynamic_cast<CKeyValue *>(cube_space->take(&key, keyval));
			}
			else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE_IF_EXISTS) {
				keyval = dynamic_cast<CKeyValue *>(cube_space->takeIfExists(&key, keyval));
			}
		}
		else if(bulk_limit<0&&data_key==NULL) { // just add
			if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE) keyval = dynamic_cast<CKeyValue *>(cube_space->take(keyval));
			else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE_IF_EXISTS) keyval = dynamic_cast<CKeyValue *>(cube_space->takeIfExists(keyval));
		}
		// take bulk (k,v) pairs
		else if(bulk_limit>0) {
			CKeyValue *any_key = NULL;

			if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE) bulk_kv = cube_space->take(any_key, bulk_limit, bulk_kv);
			else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE_IF_EXISTS) bulk_kv = cube_space->takeIfExists(any_key, bulk_limit, bulk_kv);
		}

		if(bulk_limit<0&&keyval==NULL) {
#ifdef DEBUG
			if(data_key!=NULL) cout << "there is nothing to take by key[" << dynamic_cast<CKeyValue *>(data_key)->getKey();
			else cout << "there is nothing to take by key[anykey";
			cout << "]: current size = " << cube_space->size() << endl;
#endif
			pobjs->__size = 0;
			pobjs->__ptr = NULL;

			return -1;
		}
		// check bulk data
		else if(bulk_limit>0&&bulk_kv.size()<=0) {
			pobjs->__size = 0;
			pobjs->__ptr = NULL;

			return -1;
		}


		if(bulk_limit<0) {
			pobjs->__size = 1;
			pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

			pobjs->__ptr[0] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);

#ifdef DEBUG
			cout << "#######################################" << endl;
			cout << "found key = " << keyval->getKey() << endl;
			cout << "found value = " << keyval->getValue() << endl;
			cout << "#######################################" << endl;
#endif 

			dynamic_cast<cube__CKeyValue*>(pobjs->__ptr[0])->set(keyval->getKey(), keyval->getValue());

			delete keyval;
		}
		// return bulk (k,v) pairs
		else if(bulk_limit>0) {
			pobjs->__size = bulk_kv.size();
			pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

			for(int i=0;i<pobjs->__size;i++) {
				keyval = dynamic_cast<CKeyValue *>(&(*(bulk_kv[i])));

				pobjs->__ptr[i] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);
#ifdef DEBUG
				cout << "#######################################" << endl;
				cout << "found key = " << keyval->getKey() << endl;
				cout << "found value = " << keyval->getValue() << endl;
				cout << "#######################################" << endl;
#endif 
				dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[i])->set(keyval->getKey(), keyval->getValue());
			}
		}
	}
	else {
#ifdef DEBUG
		cout << "type is not correct : " << typeid(*data_key) << endl;
#endif
		pobjs->__size = 0;
		pobjs->__ptr = NULL;

		return -1;
	}

#ifdef DEBUG
	vector< CSmartTemplate<CObject> > all;
	all = cube_space->read(NULL, all);
	cout << "-------------- read : all objects ---------------" << endl;
	cout << "size = " << all.size() << endl;
	for(int i=0;i<all.size();i++) {
		if(typeid(*data_key)==typeid(cube__CMessage)) dynamic_cast<CMessage &>(*(all[i])).print();
		else if(typeid(*data_key)==typeid(cube__CKeyValue)) dynamic_cast<CKeyValue &>(*(all[i])).print();
	}
	cout << "-------------------------- ---------------" << endl << endl;
#endif

	return 1;
}

int CSpaceService::size(map<string,string> &params, CObjectArray *objs, int start, CObjectArray *&pobjs)
{
	int size = cube_space->size();

	pobjs->__size = 1;
	pobjs->__ptr = (cube__CObject **)soap_malloc(soap, (pobjs->__size)*sizeof(*pobjs->__ptr));

	pobjs->__ptr[0] = (cube__CKeyValue *)soap_new_cube__CKeyValue(soap, -1);
	char ssize[50];
	sprintf(ssize, "%d", size);
	dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[0])->setKey(CUBE_SPACE_SIZE);
	dynamic_cast<cube__CKeyValue *>(pobjs->__ptr[0])->setValue(ssize);

	return 1;
}

void CSpaceService::run(cube__CObject *in, struct cube__invokeResponse &res) 
{
	CObjectArray *objs = dynamic_cast<CObjectArray *>(in);
	if(objs==NULL) {
#ifdef DEBUG
		cout << "### Error : input type must be an array(current type=" << typeid(*in) << ")" << endl;
#endif

		return;
	}

	CObjectArray *pobjs = (CObjectArray *)soap_new_CObjectArray(soap, -1);


	map<string,string> params;

	int start = parameterize(objs, params);
	if(start<0) {
		pobjs->__size = 0;
		pobjs->__ptr = NULL;

		res.out = pobjs;

		return;
	}

#ifdef DEBUG
	cout << "### command = " << params[CUBE_SPACE_ACTION] << endl;
#endif

	if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_WRITE) write(params, objs, start, pobjs);
	else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_READ) read(params, objs, start, pobjs, true);
	else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_READ_IF_EXISTS) read(params, objs, start, pobjs, false);
	else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE) take(params, objs, start, pobjs, true);
	else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_TAKE_IF_EXISTS) take(params, objs, start, pobjs, false);
	else if(params[CUBE_SPACE_ACTION]==CUBE_SPACE_SIZE) size(params, objs, start, pobjs);
	else {
#ifdef DEBUG
	cout << "### Error : there is no command" << endl;
#endif
		pobjs->__size = 0;
		pobjs->__ptr = NULL;
	}

#ifdef DEBUG
	cout << "######################## [begin]space service final ###########################" << endl;
	CObjectArray *array = dynamic_cast<CObjectArray *>(pobjs);
	for(int i=0;i<array->__size;i++) {
		cube__CKeyValue *keyval = dynamic_cast<cube__CKeyValue *>(array->__ptr[i]);
		cout << "[" << i << "]" << keyval->getKey() << endl;
		cout << keyval->getValue() << endl;
		cout << endl;
	}
	cout << "######################## [end]space service final ###########################" << endl;
#endif

	res.out = pobjs;
}
